Skip to content

fix(audit): bind subscribe to live plan terms (MULT-6)#35

Merged
dev-jodee merged 6 commits into
audit/ai-scanner/05-sponsor-revoke-driftfrom
audit/ai-scanner/06-subscribe-terms-binding
Apr 28, 2026
Merged

fix(audit): bind subscribe to live plan terms (MULT-6)#35
dev-jodee merged 6 commits into
audit/ai-scanner/05-sponsor-revoke-driftfrom
audit/ai-scanner/06-subscribe-terms-binding

Conversation

@dev-jodee
Copy link
Copy Markdown
Collaborator

Audit finding: MULT-6

A subscriber's pre-signed subscribe transaction could be enrolled into a recreated plan with different terms. The instruction did not bind the subscriber's intent to the plan terms they consented to at signing time, so a merchant who deleted and recreated the plan with a different amount, period, or mint could capture an old signed subscribe and lock the subscriber into the new terms.

Fix

SubscribeData now carries the four plan fields that determine financial commitment:

  • expected_mint: Address
  • expected_amount: u64
  • expected_period_hours: u64
  • expected_created_at: i64

After loading the live plan, subscribe::process rejects with PlanTermsMismatch if any field disagrees.

Consumers updated

  • clients/typescript buildSubscribe requires the four expected_* params
  • Webapp subscribe mutation takes them and plan-card.tsx passes from the live plan
  • apps/web Subscribe form exposes them as fields (low-level dev tool)
  • Test Subscribe::execute snapshots live plan terms

Test plan

  • cargo build -p subscriptions
  • cargo test -p subscriptions --lib (213/213 pass; one new regression test)
  • New subscribe_rejects_stale_expected_terms asserts mismatched expected_amount returns PlanTermsMismatch
  • pnpm --filter @subscriptions/client build
  • pnpm exec tsc --noEmit in webapp and apps/web

Stack

Stacked on top of #34 (MULT-5).

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

Compute Unit Report

Instruction Samples Min CUs Max CUs Avg CUs Est Cost (Low) [SOL] Est Cost (Med) [SOL] Est Cost (High) [SOL]
cancel_subscription 11 1774 2098 1982 0.000005000 0.000005079 0.000005991
close_subscription_authority 7 1866 1901 1871 0.000005000 0.000005074 0.000005935
create_fixed_delegation 36 3564 9595 4930 0.000005001 0.000005197 0.000007465
create_plan 84 3525 12538 4956 0.000005001 0.000005198 0.000007478
create_recurring_delegation 25 3587 20092 5274 0.000005001 0.000005210 0.000007637
delete_plan 8 401 401 401 0.000005000 0.000005016 0.000005200
init_subscription_authority 136 5805 18274 9332 0.000005002 0.000005373 0.000009666
revoke_delegation 19 303 570 405 0.000005000 0.000005016 0.000005202
subscribe 21 6639 14139 7863 0.000005002 0.000005314 0.000008931
transfer_fixed 6 8478 8481 8480 0.000005002 0.000005339 0.000009240
transfer_recurring 17 8566 8651 8594 0.000005002 0.000005343 0.000009297
transfer_subscription 10 8862 8985 8901 0.000005002 0.000005356 0.000009450
update_plan 21 409 488 461 0.000005000 0.000005018 0.000005230

Generated: 2026-04-28

Extend SubscribeData with expected_mint/amount/period_hours/created_at.
Program rejects with PlanTermsMismatch if the live plan disagrees with
what the subscriber signed. Stale-signed subscribe transactions can no
longer enroll into a recreated plan with different terms.

SDK and webapp callers fetch plan data and pass the snapshot.
@dev-jodee dev-jodee force-pushed the audit/ai-scanner/06-subscribe-terms-binding branch from 33dfaf3 to 9a6c2b0 Compare April 28, 2026 15:51
@dev-jodee dev-jodee marked this pull request as ready for review April 28, 2026 16:24
Plan::check_destination and Plan::can_pull now filter out zero-padded
slots before membership tests. A plan with fewer than four configured
destinations no longer authorizes a zero-owned receiver, and a plan
with fewer than four pullers no longer authorizes a zero-pubkey caller.
…-10)

Webapp exit flows now pass the on-chain payer as receiver when it
differs from the connected signer, so sponsor-funded delegations and
SubscriptionAuthority accounts can actually be closed.

Also migrates revokeSubscription and cancelAndRevokeSubscription from
buildRevokeDelegation to buildRevokeSubscription with planPda + receiver,
fixing subscription revoke for both sponsor and non-sponsor cases.
…(MULT-9)

Stale-delegation cleanup no longer appends a close on the current
SubscriptionAuthority. Revoking stale delegations is now scoped to the
supplied delegation accounts; the SA stays open and current grants
remain valid.
@dev-jodee dev-jodee merged commit 3b4e62c into audit/ai-scanner/05-sponsor-revoke-drift Apr 28, 2026
6 checks passed
@dev-jodee dev-jodee deleted the audit/ai-scanner/06-subscribe-terms-binding branch April 28, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant